package com.smsc.headend.task.engine.handler.impl.firmware;

import cn.hutool.core.convert.Convert;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.utils.RedisKeys;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.data.dto.TaskDataDTO;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.fwu.entity.TemplateFwUpgrade;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.ProtocolType;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.firmware.FirmwareService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * @author liangli
 * @date 2020/9/25
 */
public class ImageBlockTransferHandler implements TaskHandler {
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    RedisUtils redisUtils;
    @Value("#{T(java.lang.Integer).parseInt('${fwu.step.retryTimes:2}')}")
    Integer firmwareStepsRetry;
    @Autowired
    AssetService assetService;
    @Autowired
    FirmwareService firmwareService;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData cosemData, String actionResult) throws UDISTaskHandlerException {
        Map<String, Object> paramMap = this.getParameterMap(task, atomicTask);

        Integer blockSize;
        ComConfigDMO comConfigDMO = assetManagementRemoteService.getComConfigById(task.getComId());
        TaskDataDTO resendStatusDataDTO = null;
        if(comConfigDMO.getProtocolId() == ProtocolType.ANSI_C12_18_Protocol.getProtocolId()) {
            blockSize = Convert.toInt(assetService.getSystemProperty("MAX_BLOCK_SIZE"));
        } else {
            List<TaskDataDTO> dataDTOS = taskDAO.getAllTaskData(task.getTaskNo());
            TaskDataDTO dataDTO = dataDTOS.stream().filter(taskDataDTO -> taskDataDTO.getKey().equalsIgnoreCase("blockSize")).findAny().get();
            blockSize = Integer.valueOf(String.valueOf(dataDTO.getValue()));
            resendStatusDataDTO = dataDTOS.stream().filter(taskDataDTO -> taskDataDTO.getKey().equalsIgnoreCase("resendStatus")).findAny().orElse(null);
        }

        Long fwFileId = Convert.toLong(paramMap.get("fwFileId"));
        String fwCacheKey = RedisKeys.getTaskFirmwareFile(fwFileId, blockSize);
        Set<String> keys = redisUtils.hKeys(fwCacheKey);
        Integer totalBlockNumber = keys.size();

        AtomicTaskState atomicTaskState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
        log.debug("firmware transfer block completed comId={} meterId={}, blockNumber={} ", task.getComId(), task.getMeterId(), atomicTaskState.getBlockNumber());
        if (resendStatusDataDTO == null && atomicTaskState.getBlockNumber() < totalBlockNumber - 1) {
            atomicTaskState.setBlockNumber(atomicTaskState.getBlockNumber() + 1);
            taskDAO.hashSetAtomicTaskState(atomicTask.getAtomicTaskNo(), task, atomicTaskState.getRetryTimes(), atomicTaskState);
            return 0;
        }

        if (resendStatusDataDTO != null) {
            return 1;
        }
        log.debug("firmware transfer all block completed comId={} meterId={}, total blockNumber={} ", task.getComId(), task.getMeterId(), totalBlockNumber);
        // ANSI直接结束
        if(comConfigDMO.getProtocolId() == ProtocolType.ANSI_C12_18_Protocol.getProtocolId()) {
            firmwareService.saveFwuReport(task, atomicTask, 1, TemplateFwUpgrade.TaskType.ImageTransfer, null, null);
        }
        //block transfer over, next verify completeness
        return 1;
    }

//    private void addAtomicTaskForNextToTransfer(Task task, AtomicTask atomicTask) {
//        log.info("firmware transfer task {}  get next not transfer block", task.getTaskNo());
//        AtomicTask transferTask = task.getSubTaskGroups().stream().flatMap(subTaskGroup -> subTaskGroup.getSubTasks().stream()).filter(aTask ->
//                aTask.getOperationId().intValue() == OperationName.GetImageNotTransferLastBlock.getId()
//        ).findFirst().orElse(null);
//        AtomicTask resendBlockAtomicTask = new AtomicTask();
//        BeanUtils.copyProperties(transferTask, resendBlockAtomicTask, "atomicTaskNo");
//        resendBlockAtomicTask.setAtomicTaskNo(UUID.randomUUID().toString());
//        taskDAO.atomicTaskQueueAddReverse(resendBlockAtomicTask);
//    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.error("Image block transfer failed, comId={},meterId={}， message={}", task.getComId(), task.getMeterId(), message);
        if (firmwareService.isRetryMessageType(message)) {
            AtomicTaskState atomicTaskState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
            if (atomicTaskState.getRetryTimes() >= firmwareStepsRetry) {
                log.error("Image transfer failed retry times over");
                firmwareService.saveFwuReport(task, atomicTask, -1, TemplateFwUpgrade.TaskType.ImageTransfer, "block transfer failed", null);
                return -1;
            }
            log.error("Image transfer failed retry times {}", atomicTaskState.getRetryTimes());
            atomicTaskState.setStored(false);
            taskDAO.hashSetAtomicTaskState(atomicTask.getAtomicTaskNo(), task, atomicTaskState.getRetryTimes() + 1,  atomicTaskState);
            return 0;
        }
        log.error("stopFwtask, comId={},meterId={},type={}, messsage={}", task.getComId(), task.getMeterId(), task.getTaskType(), message);
        firmwareService.saveFwuReport(task, atomicTask, -1, TemplateFwUpgrade.TaskType.ImageTransfer, "block transfer failed", null);
        return -1;
    }
}
